package com.tsinghua.server.common.utils;


import com.tsinghua.server.common.constant.BaseRedisKeyModular;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * redis工具类
 */
@Slf4j
@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 判断 Key 是否存在
     *
     * @param key String key
     * @return boolean
     */
    public boolean hasKey(String key) {
        try {
            Boolean hasKey = redisTemplate.hasKey(key);
            return null != hasKey ? hasKey : false;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    /**
     * 添加 Key 缓存
     *
     * @param key   String key
     * @param value Object
     */
    public void setKey(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 添加 Key 缓存,并设置失效时间
     *
     * @param key   String key
     * @param value Object
     * @param time  Time
     * @param unit  TimeUnit
     */
    public void setKey(String key, Object value, long time, TimeUnit unit) {
        try {
            redisTemplate.opsForValue().set(key, value, time, unit);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 获取 Key 缓存
     *
     * @param key String key
     * @param <T> T
     * @return T
     */
    public <T> T getKey(String key, Class<T> type) {
        try {
            Object object = redisTemplate.opsForValue().get(key);
            return OrikaMapper.map(object, type);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 获取 Keys 缓存
     *
     * @param keys String key array
     * @param <T>  T
     * @return T Array
     */
    public <T> List<T> getKeys(List<String> keys, Class<T> type) {
        try {
            List<Object> objects = redisTemplate.opsForValue().multiGet(keys);
            return OrikaMapper.mapList(objects, Object.class, type);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 删除 Keys 缓存
     *
     * @param keys Key Array
     */
    public void removeKey(String... keys) {
        if (null != keys && keys.length > 0) {
            try {
                if (keys.length == 1) {
                    redisTemplate.delete(keys[0]);
                } else {
                    redisTemplate.delete(Arrays.asList(keys));
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 指定键值失效时间
     *
     * @param key  String key
     * @param time Time
     * @param unit TimeUnit
     */
    public void expire(String key, long time, TimeUnit unit) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, unit);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 获取 Key 失效时间
     *
     * @param key  String key
     * @param unit TimeUnit
     * @return long
     */
    public long expire(String key, TimeUnit unit) {
        try {
            Long expire = redisTemplate.getExpire(key, unit);
            return null != expire ? expire : 0L;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return 0L;
    }


    /**
     * <p>生成统一格式以英文冒号分割的redis主键key</p>
     *
     * <pre>
     *     所有redis的key应该使用此方法生成统一的主key
     *     若keyPrefix == null || keyBody == null都将返回null
     * </pre>
     * <pre>
     *     RedisUtil.generateRedisKey(null, 1234, "132") = null
     *     RedisUtil.generateRedisKey(null, null, "132") = null
     * 	   RedisUtil.generateRedisKey("sysuser", null) = null
     *     RedisUtil.generateRedisKey("sysuser", 1234) = sysuser:1234
     *     RedisUtil.generateRedisKey("sysuser", "zhangsan", "132") = sysuser:zhangsan:132
     * </pre>
     *
     * @param redisKeyModular : BaseRedisKeyModular : 模块枚举类的实例 : 可以按照模块进行划分
     * @param keyBody         : Object : key的主体 : 子功能
     * @param keyArgs         : String... : 可变参数数组 : 若以上两个不能满足需求可以继续进行添加
     * @return
     */
    public static String generateRedisKey(BaseRedisKeyModular redisKeyModular, Object keyBody, String... keyArgs) {
        if (redisKeyModular == null || StringUtil.isEmpty(redisKeyModular.getName())) {
            return null;
        }
        return generateRedisKey(redisKeyModular.getName(), keyBody, keyArgs);
    }

    /**
     * <p>生成统一格式以英文冒号分割的redis主键key</p>
     *
     * <pre>
     *     所有redis的key应该使用此方法生成统一的主key
     *     若keyPrefix == null || keyBody == null都将返回null
     * </pre>
     * <pre>
     *     RedisUtil.generateRedisKey(null, 1234, "132") = null
     *     RedisUtil.generateRedisKey(null, null, "132") = null
     * 	   RedisUtil.generateRedisKey("sysuser", null) = null
     *     RedisUtil.generateRedisKey("sysuser", 1234) = sysuser:1234
     *     RedisUtil.generateRedisKey("sysuser", "zhangsan", "132") = sysuser:zhangsan:132
     * </pre>
     *
     * @param keyPrefix : String : key的前缀 : 可以按照模块进行划分
     * @param keyBody   : Object : key的主体 : 子功能
     * @param keyArgs   : String... : 可变参数数组 : 若以上两个不能满足需求可以继续进行添加
     * @return
     */
    public static String generateRedisKey(String keyPrefix, Object keyBody, String... keyArgs) {
        if (StringUtil.isEmpty(keyPrefix)) {
            return null;
        }
        if (BaseUtil.isNull(keyBody)) {
            return null;
        }
        List<String> argList = getRedisKeyList(keyPrefix, String.valueOf(keyBody), keyArgs);
        // 将arrayList转换为数组
        String[] args = argList.toArray(new String[argList.size()]);
        return StringUtil.generateKeyUseColonSeparator(args);
    }

    /**
     * 获取rediskey列表
     */
    private static List<String> getRedisKeyList(String keyPrefix, String keyBody, String... keyArgs) {
        List<String> argList = new ArrayList<>();
        argList.add(keyPrefix);
        argList.add(String.valueOf(keyBody));
        if (ArrayUtil.isNotEmpty(keyArgs)) {
            for (String keyArg : keyArgs) {
                argList.add(keyArg);
            }
        }
        return argList;
    }

}
